/*
* Portuguese eID Card API .NET Wrapper
*
* Written by Fernando Nunes (fernando.nunes@devscope.net)
* Copyright (c) 2009 DevScope Information Systems
*
* This library is free software; you can redistribute it and/or 
* modify it under the terms of the GNU Lesser General Public 
* License as published by the Free Software Foundation; either 
* version 2.1 of the License, or (at your option) any later version. 
* 
* This library is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
* Lesser General Public License for more details. 
* 
* You should have received a copy of the GNU Lesser General Public 
* License along with this library; if not, write to the Free Software 
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 
*/

#include "Interop.h"

using namespace System;

namespace DevScope
{	
	namespace CartaoDeCidadao
	{
		/// <summary>
		/// SmartCard Reader and Card Watcher Daemon
		/// </summary>
		///	<remarks>Thread-Safe Lazy-Start Daemon using singleton pattern that monitorizes and notifies of system changes on SmartCard Readers and Cards</remarks>
		public ref class SCWatcher sealed
		{
			public:
				/// <summary>
				/// Callback function to be called when an error occurs
				/// </summary>
				delegate void ErrorHandler(long errorCode, String^ errorMessage);
				/// <summary>
				/// Callback function to be called when a Card is inserted into a Reader
				/// </summary>
				delegate void CardInsertedHandler(String^ readerName, String^ cardName);
				/// <summary>
				/// Callback function to be called when a Card is removed
				/// </summary>
				delegate void CardRemovedHandler(String^ readerName);
				/// <summary>
				/// Callback function to be called when a Reader is attached to the system
				/// </summary>
				delegate void ReaderInsertedHandler(String^ readerName);
				/// <summary>
				/// Callback function to be called when a Reader is detached from the system
				/// </summary>
				delegate void ReaderRemovedHandler(String^ readerName);

				/// <summary>
				/// Event fired when a SmartCard is inserted into a Reader
				/// </summary>
				event CardInsertedHandler^ CardInserted;
				/// <summary>
				/// Event fired when a SmartCard is removed from a Reader
				/// </summary>
				event CardRemovedHandler^ CardRemoved;
				/// <summary>
				/// Event fired when a SmartCard Reader is plugged-in to the system
				/// </summary>
				event ReaderInsertedHandler^ ReaderInserted;
				/// <summary>
				/// Event fired when a SmartCard Reader is removed from the system
				/// </summary>
				event ReaderRemovedHandler^ ReaderRemoved;
				/// <summary>
				/// Event fired when an error occurs
				/// </summary>
				event ErrorHandler^ OnError;

				/// <summary>
				/// Singleton Instance
				/// </summary>
				static SCWatcher^ Instance;
				/// <summary>
				/// Instantiates and starts the Daemon or just retrieves the current instance of the Daemon
				/// </summary>
				/// <returns>Returns Daemon instance</returns>
				static SCWatcher^ GetInstance();

				/// <summary>
				/// Starts the Daemon
				/// </summary>
				void Start();
				/// <summary>
				/// Stops the Daemon
				/// </summary>
				void Stop();
				/// <summary>
				/// Retrieves a list of the currently detected SmartCard Readers on the system
				/// </summary>
				/// <returns>Returns a Array of String containing the Readers' name</returns>
				array<String^>^ ListReaders();

				/// <summary>
				/// Retrieves the State of the Daemon
				/// </summary>
				property bool Stopped { bool get(); };
				/// <summary>
				/// Retrieves the work Status of the Daemon
				/// </summary>
				property bool Idle { bool get(); };

			private:
				SCWatcher();
				bool idled;
				bool stopped;
				
				static initonly Object^ instanceLock = gcnew Object();
				static initonly Object^ idleLock = gcnew Object();
				static initonly Object^ stopLock = gcnew Object();
				System::Threading::Thread^ workerThread;
				void DoWork();
				void StartDaemon();

				String^ GetSCError(long errorCode);
				DWORD CountCardsInList(LPTSTR cardList);
				String^ GetATRString(BYTE* atrBytes, DWORD atrLen);
				SCARD_READERSTATE* GetReaderChanges(SCARDCONTEXT hSC, SCARD_READERSTATE* readerList, LPDWORD listCount, LPDWORD cch);
		};
	}
}